import * as THREE from "three"
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { TWEEN } from 'three/examples/jsm/libs/tween.module.min.js';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
import './style.scss';
// TWEEN 是用于生成页面补间动画的库，在本页面中使用它实现场景镜头切换和元素位移动画效果；
// DRACOLoader 用于加载压缩过的模型，是提高页面加载速率和性能的关键；
// GLTFLoader 用于加载 .gltf 或 .glb 格式的模型。
interface initGeometryOption {
    geometryOption: {
        name: string,
        sizes?: number[],
        option?: Array<any>,
        vertices?: Array<number>
    },
    materialOption: {
        name: string,
        color?: number | string,
        opacity?: number,
        transparent?: boolean,
        specular?: string,
        shininess?: number,
        size?: number,
        side?: any,
        wireframe?: any
    },
    objectName?: string,
    beforeAdd?: Function | null
}

class Box {
    private scene;
    private renderer1;
    private renderer2;
    private main;
    private container1;
    private container2;
    private camera1;
    private camera2;
    private loadingManager;
    private secondContainer = false;
    private cursor = { x: 0, y: 0 };
    private cameraGroup;
    private pointLight;

    constructor() { }
    init(option) {
        let { main, container1, container2 } = option;
        this.main = main;
        this.container1 = container1;
        this.container2 = container2;

        // loading
        this.loading();
        // 设置渲染器
        this.setRender();
        // 初始化场景
        this.setScene();
        //初始化模型
        this.initObjects();
        // 设置相机    
        this.setCamera();
        // 启动渲染
        this.render();
        //监听屏幕位置
        this.observerScreen();
        // 动作监听
        this.addControl();
        //鼠标添加虚拟光标
        this.addMouseIcon();
        // 监听屏幕变动
        this.screenResize();
        // 鼠标悬浮到菜单动画
        this.menuAnimation();
    }

    // 鼠标和键盘事件
    addControl() {
        var controls = new OrbitControls(this.camera1, this.renderer1.domElement);//创建控件对象
    }
    // 场景
    setScene() {
        this.scene = new THREE.Scene();
        this.scene.background = "#ffffff";

        //直射光
        this.createLight("DirectionalLight", (point) => point.position.set(-100, 0, -100), "#ffffff", 0.8);

        //点光源
        this.pointLight = this.createLight("PointLight", (point) => point.position.set(30, 3, 1.8), "#88ffee", 2.7, 4, 3);

    }
    initObjects() {
        const dracoLoader = new DRACOLoader();
        dracoLoader.setDecoderPath('/draco/');
        dracoLoader.setDecoderConfig({ type: 'js' });

        const loader = new GLTFLoader(this.loadingManager);
        loader.setDRACOLoader(dracoLoader);

        // const group = new THREE.Group();
        // this.scene.add(group);
        let oldMaterial;
        // /data/cat_c32_marine_engine.glb
        loader.load("/data/statue.glb", (gltf) => {
            console.log(gltf);

            gltf.scene.traverse((obj: any) => {
                if (obj.isMesh) {
                    oldMaterial = obj.material;
                    obj.material = new THREE.MeshPhongMaterial({
                        shininess: 100
                    })
                }
            })
            this.scene.add(gltf.scene);
            oldMaterial.dispose();
            this.renderer1.renderLists.dispose();
        });
    }
    // 相机
    setCamera() {
        var width = this.main.offsetWidth; //窗口宽度
        var height = this.main.offsetHeight; //窗口高度
        var k = width / height; //窗口宽高比
        var s = 35; //三维场景显示范围控制系数，系数越大，显示的范围越大

        //创建相机对象1
        this.cameraGroup = new THREE.Group();
        this.scene.add(this.cameraGroup);
        this.camera1 = new THREE.PerspectiveCamera(s, k, 0.1, 1000);
        this.camera1.position.set(19, 1.54, -.1); //设置相机位置
        this.cameraGroup.add(this.camera1);

        //创建相机对象2
        this.camera2 = new THREE.PerspectiveCamera(s, k, 0.1, 1000);
        this.camera2.position.set(3.2, 2.8, 3.2);
        this.camera2.rotation.set(0, 1, 0);
        this.scene.add(this.camera2);
    }
    // 渲染器
    setRender() {
        // 初始化渲染器1
        let width = this.main.offsetWidth;
        let height = this.main.offsetHeight;
        this.renderer1 = new THREE.WebGLRenderer({
            canvas: this.container1,
            antialias: true,
            alpha: true,
            powerPreference: 'high-performance'
        });
        this.renderer1.setSize(width, height);
        this.renderer1.setPixelRatio(Math.min(window.devicePixelRatio, 2));
        this.renderer1.autoClear = true;
        this.renderer1.outputEncoding = THREE.sRGBEncoding;

        // 初始化渲染器2
        this.renderer2 = new THREE.WebGLRenderer({
            canvas: this.container2,
            antialias: false
        });
        this.renderer2.setPixelRatio(Math.min(window.devicePixelRatio, 2));
        this.renderer2.setSize(width, height);
        this.renderer2.outputEncoding = THREE.sRGBEncoding;
    }
    private clock = new THREE.Clock();
    private previousTime = 0;
    // 渲染
    render() {
        const elapsedTime = this.clock.getElapsedTime();
        const deltaTime = elapsedTime - this.previousTime;
        this.previousTime = elapsedTime;
        const parallaxY = this.cursor.y;
        const parallaxX = this.cursor.x
        this.pointLight.position.y -= (parallaxY * 9 + this.pointLight.position.y - 2) * deltaTime;
        this.pointLight.position.x += (parallaxX * 8 - this.pointLight.position.x) * 2 * deltaTime;
        this.cameraGroup.position.z -= (parallaxY / 3 + this.cameraGroup.position.z) * 2 * deltaTime;
        this.cameraGroup.position.x += (parallaxX / 3 - this.cameraGroup.position.x) * 2 * deltaTime;

        TWEEN.update();
        if (this.secondContainer) {
            this.renderer2.render(this.scene, this.camera2);
        } else {
            this.renderer1.render(this.scene, this.camera1);
        }
        //监听鼠标、键盘事件
        requestAnimationFrame(this.render.bind(this));
    }
    // 创建光源
    createLight(name, beforeAdd = null, ...option) {
        var point = new THREE[name](...option);
        beforeAdd && beforeAdd(point);
        this.scene.add(point); //点光源添加到场景中
        return point;
    }
    //加载动画管理
    loading() {
        this.loadingManager = new THREE.LoadingManager();
        this.loadingManager.onLoad = () => {
            (document.querySelector('.content') as any).style.visibility = 'visible';
            const yPosition = { y: 0 };
            const ftsLoader = document.querySelector('.lds-roller');
            const loadingCover = document.getElementById('loading-text-intro');
            // 隐藏加载页面动画
            new TWEEN.Tween(yPosition)
                .to({ y: 100 }, 900)
                .easing(TWEEN.Easing.Quadratic.InOut)
                .start()
                .onUpdate(() => {
                    loadingCover.style.setProperty('transform', `translate(0, ${yPosition.y}%)`)
                })
                .onComplete(function () {
                    loadingCover.parentNode.removeChild(document.getElementById('loading-text-intro'));
                    TWEEN.remove(this);
                });
            // 第一个页面相机添加入场动画
            new TWEEN.Tween(this.camera1.position.set(0, 4, 2))
                .to({ x: 0, y: 2.4, z: 5.8 }, 3500)
                .easing(TWEEN.Easing.Quadratic.InOut)
                .start()
                .onComplete(function () {
                    TWEEN.remove(this)
                    document.querySelector('.header').classList.add('ended')
                    document.querySelector('.description').classList.add('ended')
                })
            this.camera1.rotation.set(0, 0, 0);
            ftsLoader.parentNode.removeChild(ftsLoader);
            window.scroll(0, 0)

        }
    }
    observerScreen() {
        const ob = new IntersectionObserver(payload => {
            this.secondContainer = payload[0].intersectionRatio > 0.05;
        }, { threshold: 0.05 });
        ob.observe(this.container2);
    }
    // 鼠标移动时添加虚拟光标
    addMouseIcon() {
        let that = this;
        document.addEventListener('mousemove', event => {
            event.preventDefault();
            this.cursor.x = event.clientX / window.innerWidth - .5;
            this.cursor.y = event.clientY / window.innerHeight - .5;
            (document.querySelector('.cursor') as any).style.cssText = `left: ${event.clientX}px; top: ${event.clientY}px;`;
        }, false);

        // 相机动画
        function animateCamera(position, rotation) {
            new TWEEN.Tween(that.camera2.position)
                .to(position, 1800)
                .easing(TWEEN.Easing.Quadratic.InOut)
                .start()
                .onComplete(function () {
                    TWEEN.remove(this)
                })
            new TWEEN.Tween(that.camera2.rotation)
                .to(rotation, 1800)
                .easing(TWEEN.Easing.Quadratic.InOut)
                .start()
                .onComplete(function () {
                    TWEEN.remove(this);
                });
        }

        // 页面Tab点击事件监听
        document.getElementById('one').addEventListener('click', () => {
            document.getElementById('one').classList.add('active');
            document.getElementById('three').classList.remove('active');
            document.getElementById('two').classList.remove('active');
            document.getElementById('content').innerHTML = '昨夜西风凋碧树。独上高楼，望尽天涯路。';
            animateCamera({ x: 3.2, y: 2.8, z: 3.2 }, { y: 1 });
        });

        document.getElementById('two').addEventListener('click', () => {
            document.getElementById('two').classList.add('active');
            document.getElementById('one').classList.remove('active');
            document.getElementById('three').classList.remove('active');
            document.getElementById('content').innerHTML = '衣带渐宽终不悔，为伊消得人憔悴。';
            animateCamera({ x: -1.4, y: 2.8, z: 4.4 }, { y: -0.1 });
        });

        document.getElementById('three').addEventListener('click', () => {
            document.getElementById('three').classList.add('active');
            document.getElementById('one').classList.remove('active');
            document.getElementById('two').classList.remove('active');
            document.getElementById('content').innerHTML = '众里寻他千百度，蓦然回首，那人却在灯火阑珊处。';
            animateCamera({ x: -4.8, y: 2.9, z: 3.2 }, { y: -0.75 });
        });
    }
    // 页面缩放事件监听
    screenResize() {
        window.addEventListener('resize', () => {
            let width = this.main.offsetWidth;
            let height = this.main.offsetHeight;
            let scale = width / height;
            this.camera1.aspect = scale;
            this.camera1.updateProjectionMatrix();
            this.camera2.aspect = scale;
            this.camera2.updateProjectionMatrix();
            this.renderer1.setSize(width, height);
            this.renderer2.setSize(width, height);
            this.renderer1.setPixelRatio(Math.min(window.devicePixelRatio, 2));
            this.renderer2.setPixelRatio(Math.min(window.devicePixelRatio, 2));
        });
    }
    // 鼠标悬浮到菜单动画
    menuAnimation() {
        const btn = document.querySelectorAll('nav > .a');
        function update(e) {
            const span = this.querySelector('span');
            if (e.type === 'mouseleave') {
                span.style.cssText = '';
            } else {
                const { offsetX: x, offsetY: y } = e;
                const { offsetWidth: width, offsetHeight: height } = this;
                const walk = 20;
                const xWalk = (x / width) * (walk * 2) - walk, yWalk = (y / height) * (walk * 2) - walk;
                span.style.cssText = `transform: translate(${xWalk}px, ${yWalk}px);`
            }
        }
        btn.forEach(b => b.addEventListener('mousemove', update));
        btn.forEach(b => b.addEventListener('mouseleave', update));
    }
}

export default Box;